package com.gkoudai.open.platform.client;


import com.gkoudai.open.platform.common.*;

import com.gkoudai.open.platform.entry.*;
import com.sun.istack.internal.NotNull;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;

import java.net.URI;
import java.time.Duration;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.gkoudai.open.platform.common.StringCommon.*;
import static com.gkoudai.open.platform.common.TypeEnum.*;

/**
 * @author wangqi
 * @date 2024-11-06 16:45
 */
public class HttpRequestClient {
    private final Property property;
    private final WebClient webClient;

    public HttpRequestClient(Property property) {
        this.property = property;
        HttpClient httpClient = HttpClient.create().responseTimeout(Duration.ofMillis(property.getHttpResponseTimeoutMills())); // 读取超时
        webClient = WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)).build();
    }

    /**
     * 龙虎榜-期货品种列表
     *
     * @return
     */
    public ResponseBody<List<QuoteConfig>> requestFutureDragonQuoteList(Integer page, Integer size) {
        URI uri = buildFutureDragonQuoteListUri(page, size);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<QuoteConfig>>>() {
        }).block();
    }

    /**
     * 龙虎榜-多空比
     *
     * @return
     */
    public ResponseBody<MultipleSpaceRatio> requestFutureDragonLongShortRatio(LocalDate tradeDate) {
        if (tradeDate == null) {
            tradeDate = LocalDate.now();
        }
        URI uri = buildFutureDragonLongShortRatioUri(tradeDate);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<MultipleSpaceRatio>>() {
        }).block();
    }

    /**
     * 龙虎榜-多空持仓
     *
     * @param quoteCode
     * @return
     */
    public ResponseBody<BuySellPosition> requestFutureDragonLongShortPosition(String quoteCode) {
        URI uri = buildFutureDragonLongShortPosition(quoteCode);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<BuySellPosition>>() {
        }).block();
    }

    /**
     * 龙虎榜-净持仓
     *
     * @param quoteCode
     * @return
     */
    public ResponseBody<NetPosition> requestFutureDragonNetPosition(String quoteCode) {
        URI uri = buildFutureDragonNetPosition(quoteCode);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<NetPosition>>() {
        }).block();
    }

    /**
     * 异动持仓
     *
     * @return
     */
    public ResponseBody<PositionPageResponse> requestAbnormalPosition(LocalDate date, Integer page, Integer size) {
        URI uri = buildAbnormalPositionUri(date, page, size);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<PositionPageResponse>>() {
        }).block();
    }

    /**
     * 热门榜单
     *
     * @return
     */
    public ResponseBody<DataList<Hot>> requestHotList(String categorize) {
        URI uri = buildHotListUri(categorize);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<DataList<Hot>>>() {
        }).block();
    }

    /**
     * 热门榜单
     *
     * @return
     */
    public ResponseBody<DataList<Hot>> requestHotList(HotListCategorizeEnum categorize) {
        if (Objects.isNull(categorize)) {
            return requestHotList((String) null);
        }
        return requestHotList(categorize.getCategorize());
    }

    /**
     * 品种推荐
     *
     * @param month
     * @return
     */
    public ResponseBody<DataList<BreedCycleRecommendation>> requestBreedCycleRecommendation(Integer month) {
        URI uri = buildBreedCycleRecommendationUri(month);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<DataList<BreedCycleRecommendation>>>() {
        }).block();
    }

    /**
     * 指标详情数据
     *
     * @param type
     * @param request
     * @return
     */
    public ResponseBody<List<IndicatorInfo>> requestIndicatorsDetailData(String type, List<IndicatorsDetailDataRequestBody> request) {
        URI uri = buildIndicatorsDetailDataUri(type);
        String response = webClient.post().uri(uri).header(ACCESS_TOKEN, property.getToken()).body(BodyInserters.fromValue(request)).retrieve().bodyToMono(String.class).block();
        return ResponseHandlerUtils.parseIndicatorResponse(response);
    }

    /**
     * 指标详情数据
     *
     * @param indicatorsType
     * @param request
     * @return
     */
    public ResponseBody<List<IndicatorInfo>> requestIndicatorsDetailData(IndicatorsTypeEnum indicatorsType, List<IndicatorsDetailDataRequestBody> request) {
        return requestIndicatorsDetailData(indicatorsType.getType(), request);
    }

    /**
     * 农产品指标列表
     *
     * @param category
     * @return
     */
    public ResponseBody<List<MetricInformation>> requestAgriculturalProductsInfo(String category) {
        URI uri = buildAgriculturalProductsInfoUri(category);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<MetricInformation>>>() {
        }).block();
    }

    /**
     * 农产品指标列表
     *
     * @param category
     * @return
     */
    public ResponseBody<List<MetricInformation>> requestAgriculturalProductsInfo(CategoryCommon.AgriculturalCategoryEnum category) {
        return requestAgriculturalProductsInfo(category.getCategory());
    }

    /**
     * 农产品指标详情数据
     *
     * @param request
     * @return
     */
    public ResponseBody<List<IndicatorInfo>> requestAgriculturalIndicatorsDetailData(List<IndicatorsDetailDataRequestBody> request) {
        return requestIndicatorsDetailData(IndicatorsTypeEnum.AGRICULTURAL_PRODUCTS, request);
    }

    /**
     * 能源系指标列表
     *
     * @param category
     * @return
     */
    public ResponseBody<List<MetricInformation>> requestEnergyIndicatorsInfo(String category) {
        URI uri = buildEnergyIndicatorsInfoUri(category);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<MetricInformation>>>() {
        }).block();
    }

    /**
     * 能源系指标列表
     *
     * @param category
     * @return
     */
    public ResponseBody<List<MetricInformation>> requestEnergyIndicatorsInfo(CategoryCommon.EnergyCategoryEnum category) {
        return requestEnergyIndicatorsInfo(category.getCategory());
    }

    /**
     * 能源系指标详情数据
     *
     * @param request
     * @return
     */
    public ResponseBody<List<IndicatorInfo>> requestEnergyIndicatorsDetailData(List<IndicatorsDetailDataRequestBody> request) {
        return requestIndicatorsDetailData(IndicatorsTypeEnum.ENERGY, request);
    }

    /**
     * 黑色系指标列表
     *
     * @param category
     * @return
     */
    public ResponseBody<List<MetricInformation>> requestBlackIndicatorsInfo(CategoryCommon.BlackCategoryEnum category) {
        return requestBlackIndicatorsInfo(category.getCategory());
    }

    /**
     * 黑色系指标列表
     *
     * @param category
     * @return
     */
    public ResponseBody<List<MetricInformation>> requestBlackIndicatorsInfo(String category) {
        URI uri = buildBlackIndicatorsInfoUri(category);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<MetricInformation>>>() {
        }).block();
    }

    /**
     * 黑色系指标详情数据
     *
     * @param request
     * @return
     */
    public ResponseBody<List<IndicatorInfo>> requestBlackIndicatorsDetailData(List<IndicatorsDetailDataRequestBody> request) {
        return requestIndicatorsDetailData(IndicatorsTypeEnum.BLACK, request);
    }

    /**
     * 化工系指标列表
     *
     * @param category
     * @return
     */
    public ResponseBody<List<MetricInformation>> requestChemicalIndicatorsInfo(CategoryCommon.ChemicalCategoryEnum category) {
        return requestChemicalIndicatorsInfo(category.getCategory());
    }

    /**
     * 化工系指标列表
     *
     * @param category
     * @return
     */
    public ResponseBody<List<MetricInformation>> requestChemicalIndicatorsInfo(String category) {
        URI uri = buildChemicalIndicatorsInfoUri(category);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<MetricInformation>>>() {
        }).block();
    }

    /**
     * 化工系指标详情数据
     *
     * @param request
     * @return
     */
    public ResponseBody<List<IndicatorInfo>> requestChemicalIndicatorsDetailData(List<IndicatorsDetailDataRequestBody> request) {
        return requestIndicatorsDetailData(IndicatorsTypeEnum.CHEMICAL_INDUSTRY, request);
    }

    /**
     * 行情报价
     *
     * @param type
     * @param code
     * @return
     */
    public ResponseBody<List<Quote>> requestQuotes(String type, String code) {
        URI uri = buildQuotesUri(type, code);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<Quote>>>() {
        }).block();
    }

    /**
     * 行情报价
     *
     * @param type
     * @param codes
     * @return
     */
    public ResponseBody<List<Quote>> requestQuotes(CategoryCommon.QuoteTypeEnum type, List<String> codes) {
        return requestQuotes(type.getType(), StringUtils.join(codes, ","));
    }

    /**
     * ETF
     *
     * @param typeEnum
     * @return
     */
    public ResponseBody<DataList<EtfValue>> requestEtf(CategoryCommon.EtfTypeEnum typeEnum) {
        URI uri = buildEtfUri(typeEnum.getType());
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<DataList<EtfValue>>>() {
        }).block();
    }

    /**
     * 递延方向
     *
     * @return
     */
    public ResponseBody<List<DeferredDirection>> requestDeferredDirection() {
        URI uri = buildDeferredDirection();
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<DeferredDirection>>>() {
        }).block();
    }

    /**
     * 实物金价
     *
     * @return
     */
    public ResponseBody<List<PhysicalGoldPrice>> requestPhysicalGoldPrice() {
        URI uri = buildPhysicalGoldPriceUri();
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<PhysicalGoldPrice>>>() {
        }).block();
    }

    /**
     * 股票列表
     *
     * @param pageNum
     * @param pageSize
     * @return
     */
    public ResponseBody<List<StockQuoteInfo>> requestStockList(String pageNum, String pageSize) {
        URI uri = buildStockListUri(pageNum, pageSize);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<StockQuoteInfo>>>() {
        }).block();
    }

    /**
     * 十大股东
     *
     * @param code
     * @return
     */
    public ResponseBody<List<ShareholderDataList>> requestTop10Shareholders(String code) {
        URI uri = buildTop10ShareholdersUri(code);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<ShareholderDataList>>>() {
        }).block();
    }

    /**
     * 十大流通股东
     *
     * @param code
     * @return
     */
    public ResponseBody<List<ShareholderDataList>> requestTop10OutstandingShareholders(String code) {
        URI uri = buildTop10OutstandingShareholdersUri(code);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<ShareholderDataList>>>() {
        }).block();
    }

    /**
     * 股票日线行情
     *
     * @param code
     * @return
     */
    public ResponseBody<List<StockQuote>> requestStockDailyQuotes(String code) {
        URI uri = buildStockDailyQuotesUri(code);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<StockQuote>>>() {
        }).block();
    }

    /**
     * 股票日线行情
     *
     * @param codes
     * @return
     */
    public ResponseBody<List<StockQuote>> requestStockDailyQuotes(List<String> codes) {
        return requestStockDailyQuotes(StringUtils.join(codes, ","));
    }

    /**
     * 股票每日指标
     *
     * @param code
     * @return
     */
    public ResponseBody<List<DailyMetrics>> requestStockDailyMetrics(String code) {
        URI uri = buildStockDailyMetricsUri(code);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<DailyMetrics>>>() {
        }).block();
    }

    /**
     * 股票每日指标
     *
     * @param codes
     * @return
     */
    public ResponseBody<List<DailyMetrics>> requestStockDailyMetrics(List<String> codes) {
        return requestStockDailyMetrics(StringUtils.join(codes, ","));
    }

    /**
     * 股票财务指标
     *
     * @param code
     * @return
     */
    public ResponseBody<List<FinancialMetrics>> requestStockFinancialMetrics(String code) {
        URI uri = buildStockFinancialMetricsUri(code);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<FinancialMetrics>>>() {
        }).block();
    }

    /**
     * 股票利润表
     *
     * @param code
     * @return
     */
    public ResponseBody<List<IncomeStatement>> requestStockIncomeStatement(String code) {
        URI uri = buildStockIncomeStatementUri(code);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<IncomeStatement>>>() {
        }).block();
    }

    /**
     * 股票资产负债表
     *
     * @param code
     * @return
     */
    public ResponseBody<List<BalanceSheet>> requestStockBalanceSheet(String code) {
        URI uri = buildStockBalanceSheet(code);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<BalanceSheet>>>() {
        }).block();

    }

    /**
     * 股票现金流量表
     *
     * @param code
     * @return
     */
    public ResponseBody<List<CashFlowStatement>> requestStockCashFlowStatement(String code) {
        URI uri = buildStockCashFlowStatementUri(code);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<CashFlowStatement>>>() {
        }).block();
    }

    /**
     * 资讯数据
     *
     * @param type
     * @param page
     * @param size
     * @return
     */
    public ResponseBody<Page<News>> requestNewsList(String type, String page, String size) {
        URI uri = buildNewsListUri(type, page, size);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<Page<News>>>() {
        }).block();
    }

    /**
     * 资讯数据
     *
     * @param type
     * @param page
     * @param size
     * @return
     */
    public ResponseBody<Page<News>> requestNewsList(CategoryCommon.NewsTypeEnum type, String page, String size) {
        return requestNewsList(type.getType(), page, size);
    }

    /**
     * K线数据
     *
     * @param code
     * @param cycle
     * @param count     最大值500  大于500时 也只会返回500条
     * @param timestamp
     * @return
     */
    public String requestKline(String code, Integer cycle, Integer count, Long timestamp) {
        URI uri = buildKlineUri(code, cycle, count, timestamp);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(String.class).block();
    }

    /**
     * @param code
     * @param cycle
     * @param count     最大值500  大于500时 也只会返回500条
     * @param timestamp
     * @return
     */
    public String requestKline(String code, KlineCycleEnum cycle, Integer count, Long timestamp) {
        URI uri = buildKlineUri(code, cycle.getCycle(), count, timestamp);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(String.class).block();
    }

    /**
     * 黄金要闻列表
     *
     * @param page
     * @param size
     * @return
     */
    public ResponseBody<List<GoldNews>> requestGoldNewsList(Integer page, Integer size) {
        URI uri = buildGoldNewsList(page, size);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<GoldNews>>>() {
        }).block();
    }

    /**
     * 黄金要闻详情
     *
     * @param id
     * @return
     */
    public ResponseBody<GoldNewsDetail> requestGoldNewsDetail(String id) {
        URI uri = buildGoldNewsDetail(id);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<GoldNewsDetail>>() {
        }).block();
    }

    /**
     * 期货交易所合约交割信息查询
     *
     * @param code
     * @return
     */
    public ResponseBody<List<DeliveryConfig>> requestDeliveryConfig(String code) {
        URI uri = buildDeliveryConfigUri(code);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<DeliveryConfig>>>() {
        }).block();
    }

    public ResponseBody<List<DeliveryConfig>> requestDeliveryConfig(ExchangeCodeEnum code) {
        return requestDeliveryConfig(code.getExchangeCode());

    }

    public ResponseBody<QuoteVol> requestQuoteVol(String code) {
        URI uri = buildQuoteVolUri(code);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<QuoteVol>>() {
        }).block();

    }

    public ResponseBody<List<RealTimeQuote>> requestRealTimeQuote(String code, String id, Integer size) {
        URI uri = buildRealTimeQuote(code, id, size);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<RealTimeQuote>>>() {
        }).block();
    }

    /**
     * 期货K线数据
     *
     * @param code
     * @param cycle
     * @param count     最大值500  大于500时 也只会返回500条
     * @param timestamp
     * @return
     */
    public String requestFuturesKline(String code, KlineCycleEnum cycle, Integer count, Long timestamp) {
        URI uri = buildFuturesKlineUri(code, cycle.getCycle(), count, timestamp);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(String.class).block();
    }

    /**
     * 期货行情报价
     *
     * @param type
     * @return
     */
    public ResponseBody<List<Quote>> requestFuturesQuotes(ExchangeCodeEnum type, List<String> codeList) {


        URI uri = buildFuturesQuotesUri(Objects.isNull(type) ? null : type.getExchangeCode(), Objects.isNull(codeList) || codeList.isEmpty() ? null : StringUtils.join(codeList, ","));
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<List<Quote>>>() {
        }).block();
    }

    public ResponseBody<List<Quote>> requestFuturesQuotes(ExchangeCodeEnum type) {

        return requestFuturesQuotes(type, null);
    }

    /**
     * 期货行情报价
     *
     * @param codes
     * @return
     */
    public ResponseBody<List<Quote>> requestFuturesQuotes(List<String> codes) {
        return requestFuturesQuotes(null, codes);
    }


    /**
     * 日历,事件,假期数据
     *
     * @param typeList
     * @param date
     * @return
     */
    public String requestCalendarData(@NotNull List<String> typeList, LocalDate date) {
        if (date == null) {
            date = LocalDate.now();
        }
        URI uri = buildCalendarUri(typeList, date.format(DateTimeFormatter.ISO_DATE));
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(String.class).block();
    }

    public String requestCalendarData(LocalDate date, CategoryCommon.CalendarTypeEnum... typeEnums) {
        List<String> typeList = Arrays.stream(typeEnums).map(CategoryCommon.CalendarTypeEnum::getType).collect(Collectors.toList());
        return requestCalendarData(typeList,date);

    }

    public String requestCalendarData(LocalDate date, String... types) {
        return requestCalendarData(Arrays.asList(types),date);

    }


    /**
     * 资讯变动接口
     *
     * @param type
     * @param page
     * @return
     */
    public ResponseBody<Page<NewChangeLog>> requestNewChangeList(Integer page, String... type) {
        URI uri = buildNewsChangeList(Arrays.asList(type), page);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<Page<NewChangeLog>>>() {
        }).block();
    }

    public ResponseBody<Page<NewChangeLog>> requestNewChangeList(Integer page, CategoryCommon.NewsTypeEnum... typeEnums) {
        URI uri = buildNewsChangeList(Arrays.stream(typeEnums).map(CategoryCommon.NewsTypeEnum::getType).collect(Collectors.toList()), page);
        return webClient.get().uri(uri).header(ACCESS_TOKEN, property.getToken()).retrieve().bodyToMono(new ParameterizedTypeReference<ResponseBody<Page<NewChangeLog>>>() {
        }).block();
    }
}
